<?php

namespace app\worker\library\kefu;

use ba\Date;
use Throwable;
use think\facade\Db;
use GatewayWorker\Lib\Gateway;
use app\admin\model\kefu\User;
use app\admin\model\kefu\Message;
use modules\kefu\library\StrComparison;

class Common
{
    /**
     * 运行时静态数据
     */
    public static array $runtime = [];

    /**
     * 获取游客列表
     * @throws Throwable
     */
    public static function getTourists(array $ids): array
    {
        $tourists = Db::name('kefu_user')
            ->alias('u')
            ->field('u.id, u.identity, u.nickname, u.referrer, u.create_time,s.id as session_id, s.session_user_id, bau.nickname as bau_nickname')
            ->join('kefu_session s', 's.user_id=u.id AND s.session_user_id<=0 AND s.delete_time IS NULL', 'LEFT')
            ->join('user bau', 'bau.id=u.user_id', 'LEFT')
            ->where('u.id', 'in', $ids)
            ->select()
            ->toArray();
        foreach ($tourists as $key => $tourist) {
            $tourists[$key]['trajectory_count']    = Db::name('kefu_trajectory')
                ->where('user_id', $tourist['id'])
                ->where('title', '访问')
                ->count();
            $tourists[$key]['last_trajectory_url'] = Db::name('kefu_trajectory')
                ->where('user_id', $tourist['id'])
                ->where('title', '访问')
                ->order('create_time', 'desc')
                ->value('url');
            $tourists[$key]['last_trajectory_url'] = $tourists[$key]['last_trajectory_url'] ?: '未知';

            $tourists[$key]['status']   = $tourist['session_id'] ? 'await' : 'invite';
            $tourists[$key]['nickname'] = Common::trueValue([
                $tourist['bau_nickname'] ?? '',
                $tourist['nickname'],
                self::getTouristNickname($tourist['id']),
            ]);

            // 最后消息（若有）
            if ($tourist['session_id']) {
                $tourists[$key]['last_message'] = self::getLastMessage($tourist['session_id'], $tourist['id']);
            }
        }
        return $tourists;
    }

    /**
     * 获取会话列表 - 单用户只保留前40个会话以保证性能
     * @throws Throwable
     */
    public static function getSessions(int $userId): array
    {
        $session = Db::name('kefu_session')
            ->alias('s')
            ->field('s.*, t.top')
            ->join('kefu_session_top t', "t.user_id=$userId AND t.session_id=s.id", 'LEFT')
            ->where('s.user_id|s.session_user_id', $userId)
            ->whereNull('s.delete_time')
            ->order([
                't.top'         => 'desc',
                's.sort_time'   => 'desc',
                's.create_time' => 'desc',
            ])
            ->select()
            ->toArray();

        $count         = 1;
        $delSessionIds = [];
        foreach ($session as $key => $item) {

            // 单用户只保留前40个会话以保证性能
            $count++;
            if ($count > 40) {
                $delSessionIds[] = $item['id'];
                unset($session[$key]);
                continue;
            }

            $session[$key] = self::formatSessionInfo($item, $userId);
        }

        if ($delSessionIds) {
            Db::name('kefu_session')->where('id', 'in', $delSessionIds)
                ->update([
                    'delete_time' => time(),
                ]);
        }
        return $session;
    }

    /**
     * 整理会话资料
     * @throws Throwable
     */
    public static function formatSessionInfo($session, int $userId)
    {
        // 时间
        $session['sort_time'] = Date::human($session['sort_time'] ?: $session['create_time']);

        // 会话对象信息
        $session['session_user_id'] = $session['user_id'] == $userId ? $session['session_user_id'] : $session['user_id'];
        $session['user_id']         = $userId;

        $sessionUserInfo = User::where('id', $session['session_user_id'])->find();
        if ($sessionUserInfo) {
            // 格式化会话对象资料
            $sessionUserInfo = User::outputFields(['id', 'identity', 'avatar', 'nickname', 'status', 'last_login_time', 'create_time'], $sessionUserInfo);

            // 头像、昵称、状态
            $session['avatar']                = $sessionUserInfo['avatar'];
            $session['nickname']              = $sessionUserInfo['nickname'];
            $session['session_user_status']   = $sessionUserInfo['status'];
            $session['session_user_identity'] = $sessionUserInfo['identity'];
        }

        // 最后消息
        $session['last_message'] = self::getLastMessage($session['id'], $userId);
        if (!$session['last_message']) {
            $session['last_message'] = '用户上次登录于' . ($sessionUserInfo['last_login_time'] ?? '无');
        }

        // 未读消息数
        $session['unread_message_count'] = Db::name('kefu_message')
            ->where('session_id', $session['id'])
            ->where('recipient_id', $userId)
            ->where('status', 'unread')
            ->where('delete_user_id', '<>', $userId)
            ->count();

        return $session;
    }

    /**
     * 获取最后消息
     * @throws Throwable
     */
    public static function getLastMessage(int $sessionId, int $userId)
    {
        $lastMessage = Db::name('kefu_message')
            ->where('session_id', $sessionId)
            ->where('delete_user_id', '<>', $userId)
            ->order([
                'create_time' => 'desc',
                'id'          => 'desc'
            ])
            ->find();
        return self::formatMessage($lastMessage);
    }

    /**
     * 获取未读消息
     * @throws Throwable
     */
    public static function getUnreadMessage(int $userId)
    {
        $unreadMessage = Db::name('kefu_message')
            ->where('recipient_id', $userId)
            ->where('status', 'unread')
            ->where('delete_user_id', '<>', $userId)
            ->order([
                'create_time' => 'desc',
                'id'          => 'desc'
            ])
            ->find();
        if (!$unreadMessage) return false;

        $sessionUserInfo = User::where('id', $unreadMessage['sender_id'])->find();
        if ($sessionUserInfo) {
            $sessionUserInfo           = User::outputFields(['nickname'], $sessionUserInfo);
            $unreadMessage['nickname'] = $sessionUserInfo['nickname'];
        }

        $unreadMessage['formatContent'] = self::formatMessage($unreadMessage);
        return $unreadMessage;
    }

    /**
     * 格式化消息内容
     */
    public static function formatMessage($message)
    {
        if (!$message) return '';

        $messageContent = $message['content'];

        if ($message['type'] == 'text') {
            $messageContent = preg_replace('/!\[[^\]]*\]\([^\)]*\)/', '[图片]', $messageContent);
            $messageContent = preg_replace('/\[[^\]]*\]\([^\)]*\)/', '[链接]', $messageContent);
        } elseif ($message['type'] == 'image') {
            $messageContent = '[图片]';
        } elseif ($message['type'] == 'audio') {
            $messageContent = '[音频]';
        } elseif ($message['type'] == 'video') {
            $messageContent = '[视频]';
        } elseif ($message['type'] == 'voice') {
            $messageContent = '[语音]';
        } elseif ($message['type'] == 'file') {
            $messageContent = '[文件]';
        } elseif ($message['type'] == 'link') {
            $messageContent = '[链接]';
        } elseif ($message['type'] == 'system') {
            $messageContent = '[系统消息]' . $messageContent;
        }

        return $messageContent;
    }

    /**
     * 阅读消息
     * @throws Throwable
     */
    public static function readMessage(int $sessionId, int $recipientId, int $senderId): void
    {
        Message::where('session_id', $sessionId)
            ->where('recipient_id', 'in', [$recipientId, 0])
            ->where('status', 'unread')
            ->update([
                'status' => 'read'
            ]);

        $senderInfo = User::where('id', $senderId)->find();
        self::sendMessageToUid('readMessage', [
            'sessionId' => $sessionId,
            'senderId'  => $senderId,
        ], self::mark('uid', $senderInfo));
    }

    /**
     * 加载聊天记录
     * @throws Throwable
     */
    public static function loadRecords(int $sessionId, int $userId, array $limit): array
    {
        $records = Message::where('session_id', $sessionId)
            ->where('delete_user_id', '<>', $userId)
            ->order([
                'create_time' => 'desc',
                'id'          => 'desc'
            ])
            ->limit($limit['min'], $limit['size'])
            ->select()
            ->toArray();

        $users         = [];
        $recordUserIds = [];
        foreach ($records as $record) {
            $recordUserIds[] = $record['sender_id'];
        }
        if ($recordUserIds) {
            $recordUserIds = array_unique($recordUserIds);
            $userTemp      = User::where('id', 'in', $recordUserIds)->select();
            if (!$userTemp->isEmpty()) {
                foreach ($userTemp as $user) {
                    $users[$user->id] = User::outputFields(['id', 'identity', 'avatar', 'nickname'], $user);
                }
            }
        }
        return [$records, $users];
    }

    /**
     * 将消息记录按时间进行分组
     * @param array  $message 消息记录
     * @param string $orderBy 排序方式
     * @return array
     */
    public static function groupByTime(array $message, string $orderBy = 'asc'): array
    {
        if (!$message) {
            return [];
        }

        $messageTemp = [];
        $createTime  = $message[0]['create_time'];
        foreach ($message as $value) {

            if ($orderBy == 'asc') {
                $diff = $createTime - $value['create_time'];
            } else {
                $diff = $value['create_time'] - $createTime;
            }

            if ($diff >= 600) {
                $createTime = $value['create_time'];
            }
            $messageTemp[$createTime][] = $value;
        }
        unset($message);
        foreach ($messageTemp as $key => $value) {
            $message[] = [
                'datetime' => Date::human($key),
                'data'     => $value
            ];
        }
        unset($messageTemp);
        return $message ?? [];
    }

    /**
     * 客服代表接待用户时的数据处理
     * @throws Throwable
     */
    public static function csrReceptionHandle(int $csrId, int $userId, string $type = 'inc'): void
    {
        // 未完成的接待记录
        $log = Db::name('kefu_reception_log')
            ->where('csr_id', $csrId)
            ->where('user_id', $userId)
            ->where('status', 0)
            ->find();

        // 接待
        if ($type == 'inc' && !$log) {
            // 插入接待记录
            Db::name('kefu_reception_log')
                ->insert([
                    'csr_id'      => $csrId,
                    'user_id'     => $userId,
                    'status'      => 0,
                    'create_time' => time(),
                ]);

            // 修改客服代表接待量和上次接待时间
            Db::name('kefu_user')
                ->where('id', $csrId)
                ->inc('csr_reception_count')
                ->update([
                    'csr_last_reception_time' => time()
                ]);
        }

        // 接待完成
        if ($type == 'dec' && $log) {
            // 修改接待记录
            Db::name('kefu_reception_log')
                ->where('id', $log['id'])
                ->update([
                    'status' => 1,
                ]);

            // 修改客服代表接待量
            Db::name('kefu_user')
                ->where('id', $csrId)
                ->dec('csr_reception_count')
                ->update();
        }
    }

    /**
     * 从知识库获取最佳匹配知识点
     * @throws Throwable
     */
    public static function getBestMatchKb(string $keywords)
    {
        $kbs = Db::name('kefu_kbs')
            ->where('status', 1)
            ->order('weigh', 'desc')
            ->select();

        $bestKbMatch   = 0; // 最佳匹配度
        $bestKb        = []; // 最佳匹配知识点
        $strComparison = new StrComparison();
        foreach ($kbs as $kb) {
            $problems = explode("\n", trim(str_replace("\r\n", "\n", $kb['problem'])));
            foreach ($problems as $problem) {
                $problem = trim($problem);
                $match   = $strComparison->getSimilar($keywords, $problem);
                if ($match > 0 && $match > $bestKbMatch && $match >= $kb['match']) {
                    $kb['match'] = $bestKbMatch = $match;
                    $kb['title'] = $problem;
                    $bestKb      = $kb;
                }
            }
        }

        return $bestKb;
    }

    /**
     * 发送会话消息
     * @throws Throwable
     */
    public static function sendSessionMessage(int $sessionId, string $type, string $content, array $extend, int $senderUserId): Message|bool
    {
        $session = Db::name('kefu_session')
            ->where('id', $sessionId)
            ->whereNull('delete_time')
            ->find();
        if (!$session) return false;

        // 推送用户IDs
        $userIds   = [];
        $userIds[] = $senderUserId;
        $userIds[] = $session['user_id'];
        if ($session['session_user_id']) {
            $userIds[] = $session['session_user_id'];
        }
        $userIds = array_filter($userIds);
        $userIds = array_unique($userIds);

        // 消息入库
        $message = Message::create([
            'type'         => $type,
            'session_id'   => $sessionId,
            'sender_id'    => $senderUserId,
            'recipient_id' => $session['user_id'] == $senderUserId ? $session['session_user_id'] : $session['user_id'],
            'content'      => $content,
            'extend'       => $extend,
        ]);

        // 所有用户信息的数组
        $users = [];
        foreach ($userIds as $userId) {
            $users[$userId] = User::where('id', $userId)->find();
            $users[$userId] = User::outputFields(['id', 'identity', 'avatar', 'nickname'], $users[$userId]);
        }

        // 新消息推送
        foreach ($userIds as $userId) {
            self::sendMessageToUid('newMessage', [
                'users'         => $users,
                'message'       => $message,
                'session'       => self::formatSessionInfo($session, $userId),
                'formatMessage' => self::formatMessage($message),
            ], self::mark('uid', $users[$userId]));
        }

        // 沟通建立 - 按需增加客服代表接待量
        if (!empty($message->sender_id) && !empty($message->recipient_id) && $users[$message->recipient_id] && $users[$message->sender_id]) {
            if ($users[$message->sender_id]['identity'] == 'csr' && $users[$message->recipient_id]['identity'] != 'csr') {
                self::csrReceptionHandle($message->sender_id, $message->recipient_id);
            } elseif ($users[$message->sender_id]['identity'] != 'csr' && $users[$message->recipient_id]['identity'] == 'csr') {
                self::csrReceptionHandle($message->recipient_id, $message->sender_id);
            }
        }

        // 修改会话的排序时间
        Db::name('kefu_session')
            ->where('id', $sessionId)
            ->update([
                'sort_time' => time(),
            ]);

        return $message;
    }

    /**
     * 获取一些标记符号的完整形式
     */
    public static function mark(string $type, mixed $extend = []): string
    {
        $groupTypeMap = [
            'uid'          => '%s_%s|%s',// uid绑定标识
            'csrGroup'     => '%s_csr', // 客服代表组名
            'userGroup'    => '%s_user', // 有对话的游客、会员组名
            'touristGroup' => '%s_tourist', // 无对话的游客、会员组名
            'sessionKey'   => '%s_user',
        ];
        return sprintf($groupTypeMap[$type] ?? '%s_default', 'kf', $extend['identity'] ?? '', $extend['id'] ?? '');
    }

    /**
     * 从多个输入中寻找可用值
     * @param array $attrs
     * @return mixed
     */
    public static function trueValue(array $attrs): mixed
    {
        $attrs = array_filter($attrs);
        return array_values($attrs)[0] ?? '';
    }

    public static function getTouristNickname($id): string
    {
        return '游客 ' . $id;
    }

    /**
     * 应用消息拼接
     */
    public static function appMsg(string $message): string
    {
        if (!isset(self::$runtime['app_name'])) {
            self::$runtime['app_name'] = Db::name('kefu_config')
                ->where('name', 'app_name')
                ->value('value');
        }
        return sprintf('【%s】%s', self::$runtime['app_name'], $message);
    }

    /**
     * 记录登录用户
     */
    public static function setUser(array $user): void
    {
        $_SESSION[self::mark('sessionKey')] = [
            'id'       => $user['id'],
            'identity' => $user['identity'],
        ];
    }

    /**
     * 【按需调用】
     * Workerman 的 SESSION 是异步存储到 gateway 的，如果某个客户端快速发送请求 AB 的话，A 请求设置的 SESSION 还没有成功写入 gateway ，这时候就处理 B 请求，那么 A 设置的 SESSION 可能会读取不到。
     * 此方法可强行同步当前连接的 SESSION 数据，用于同一登录用户并发请求的接口 AB
     */
    public static function getSession($clientId): void
    {
        $_SESSION = Gateway::getSession($clientId);
    }

    /**
     * 获取登录用户
     */
    public static function getUser(?string $key = null)
    {
        if (is_null($key)) {
            return $_SESSION[self::mark('sessionKey')] ?? [];
        }
        return $_SESSION[self::mark('sessionKey')][$key] ?? '';
    }

    /**
     * 获取用户当前状态（实时的）
     */
    public static function getUserCurrentStatus()
    {
        $user = self::getUser();

        if (!$user) {
            return '';
        }

        return Db::name('kefu_user')->where('id', $user['id'])->value('status');
    }

    /**
     * 修改用户状态
     * @param string $newStatus 新的状态
     * @param bool   $force     强制更新状态
     * @return string
     * @throws Throwable
     */
    public static function changeUserStatus(string $newStatus, bool $force = false): string
    {
        $user = self::getUser();
        if (!$user) {
            return '';
        }

        if ($newStatus == 'online') {
            // 用户状态为离线则上线
            $status = self::getUserCurrentStatus();
            if ($status == 'offline' || $force) {
                Db::name('kefu_user')->where('id', $user['id'])
                    ->update([
                        'status' => $newStatus,
                    ]);
                return $newStatus;
            }
            return $status;
        } elseif ($newStatus == 'offline') {
            // 用户所有连接已断开则下线
            if (!Gateway::getClientIdByUid(self::mark('uid', $user)) || $force) {
                Db::name('kefu_user')
                    ->where('id', $user['id'])
                    ->update([
                        'status' => $newStatus,
                    ]);
                return $newStatus;
            }
            return self::getUserCurrentStatus();
        } else {
            // 其他状态直接修改
            Db::name('kefu_user')->where('id', $user['id'])
                ->update([
                    'status' => $newStatus,
                ]);
            return $newStatus;
        }
    }

    /**
     * 发送 ws 消息
     */
    public static function sendMessageToUid(string $type, mixed $data, mixed $uid): void
    {
        $json = json_encode([
            'type' => $type,
            'data' => $data,
            'time' => time(),
        ]);

        if (Gateway::isUidOnline($uid)) {
            Gateway::sendToUid($uid, $json);
        }
    }
}